<?php
declare (strict_types = 1);

namespace app\tools;

use app\exception\LogicException;
use Carbon\Carbon;

final class StringTool
{
    /**
     * explode concat
     *
     * @param $string
     * @param string $delimiter
     * @return array
     */
    public function explodeImplode($string, $delimiter = ',')
    {
        return empty($string) ? [] : array_values(array_unique(explode($delimiter, implode($delimiter, $string))));
    }

    /**
     * 获取时间戳相差秒数
     *
     * @param $start
     * @param $end
     * @return float|int
     */
    public function getDiffSecondsByTimestamp($start, $end)
    {
        if (is_null($start) || is_null($end) || $start > $end) {
            return 0;
        }
        return $end - $start;
    }

    /**
     * 获取时间戳相差天数
     *
     * @param $start
     * @param $end
     * @return float|int
     */
    public function getDiffMinByTimestamp($start, $end)
    {
        if (is_null($start) || is_null($end) || $start > $end) {
            return 0;
        }
        return ceil(($end - $start) / 60);
    }

    /**
     * 获取时间戳相差天数
     *
     * @param $start
     * @param $end
     * @return float|int
     */
    public function getDiffByTimestamp($start, $end)
    {
        if (is_null($start) || is_null($end) || $start > $end) {
            return 0;
        }
        return ceil(($end - $start) / 86400);
    }

    /**
     * 单位1元换算单位1万
     *
     * @param $price
     * @param int $decimal
     * @return string|null
     */
    public function YuanToWan($price, $decimal = 2)
    {
        return bcdiv((string)$price, '10000', $decimal);
    }

    /**
     * 单位1万换算单位1元
     *
     * @param $price
     * @param int $decimal
     * @return string|null
     */
    public function WanToYuan($price, $decimal = 0)
    {
        return bcmul((string)$price, '10000', $decimal);
    }

    /**
     * 折扣计算
     *
     * @param $price
     * @param $original
     * @return string
     */
    public function discount($price, $original)
    {
        if ((int)$original == 0) {
            return '0';
        }
        $radio = bcdiv((string)$price, (string)$original, 2);
        $result = bcmul($radio, '10', 1);
        return $result > 10 ? '10' : $result;
    }

    /**
     * 百分比计算
     *
     * @param $number1
     * @param $number2
     * @return string
     */
    public function percentage($number1, $number2)
    {
        if ((int)$number2 == 0) {
            return '0';
        }
        $radio = bcdiv((string)$number1, (string)$number2, 2);
        $result = bcmul($radio, '100');
        return $result > 100 ? '100' : $result;
    }

    /**
     * 经纬度偏移量(单位公里)
     *
     * @param $distance
     * @return false|float
     */
    public function getOffsetByDistance($distance)
    {
        return (int)ceil($distance / 111);
    }

    /**
     * 获取真实客户端ip
     *
     * @return array|false|mixed|string
     */
    public function getClientIp()
    {
        if (getenv('HTTP_CLIENT_IP'))
            $ip = getenv('HTTP_CLIENT_IP');
        else if (getenv('HTTP_X_REAL_IP'))
            $ip = getenv('HTTP_X_REAL_IP');
        else if (getenv('HTTP_X_FORWARDED_FOR')) {
            $ip = getenv('HTTP_X_FORWARDED_FOR');
            $ips = explode(',', $ip);
            $ip = $ips[0];
        } else if (getenv('HTTP_X_FORWARDED'))
            $ip = getenv('HTTP_X_FORWARDED');
        else if (getenv('HTTP_FORWARDED_FOR'))
            $ip = getenv('HTTP_FORWARDED_FOR');
        else if (getenv('HTTP_FORWARDED'))
            $ip = getenv('HTTP_FORWARDED');
        else if (getenv('REMOTE_ADDR'))
            $ip = getenv('REMOTE_ADDR');
        else
            $ip = '0.0.0.0';

        return $ip;
    }

    /**
     * 转换秒
     *
     * @param $second
     * @param string $convert
     * @return false|float
     */
    public function getSecondConvert($second, $convert = 'm')
    {
        $result = $second;

        switch ($convert) {
            case 'm':
                $result = ceil((float)bcdiv((string)$second, '60'));
                break;
        }

        return $result;
    }

    /**
     * 获取一年内月份
     *
     * @param int $month
     * @return array
     */
    public function getOneYearMonth($month = 6)
    {
        $result = [];
        if ($month < 12) {
            $lessMonth = Carbon::now()->subMonths($month)->month;
            for ($i = 1; $i <= $month; $i++) {
                $result[] = (string)Carbon::now()->month($lessMonth)->addMonths($i)->month;
            }
        }
        return $result;
    }

    /**
     * 根据月数相减获取年月
     *
     * @param int $month
     * @return array
     */
    public function getYearMonthBySubMonths($month = 6)
    {
        $result = [];
        $less = Carbon::now()->startOfMonth()->subMonths($month)->timestamp;
        for ($i = 1; $i <= $month; $i++) {
            $result[] = Carbon::createFromTimestamp($less)->addMonths($i)->format('Y-m');
        }
        return $result;
    }

    /**
     * 统计月份处理 2021-01 处理为 01月
     *
     * @param $months
     * @return array
     */
    public function interceptMonthByYearMonth($months)
    {
        $result = [];
        if (!empty($months)) {
            foreach ($months as $month) {
                // $result[] = substr($month, -2) . '月';
                $result[] = substr($month, -2);
            }
        }

        return $result;
    }

    /**
     * 获取年月根据amis创建时间搜索
     *
     * @param $createtime
     * @return array
     */
    public function getYearMonthByCreatetime($createtime)
    {
        $range = explode(',', $createtime);
        $start = $range[0];
        $end = $range[1];

        $startMonth = Carbon::createFromTimestamp($start)->startOfMonth()->format('Y-m');
        $endMonth = Carbon::createFromTimestamp($end)->startOfMonth()->format('Y-m');

        $result = [$startMonth];
        while ($startMonth != $endMonth) {
            $startMonth = Carbon::createFromTimestamp($start)->startOfMonth()->addMonths()->format('Y-m');
            $start = Carbon::createFromTimestamp($start)->startOfMonth()->addMonths()->timestamp;
            $result[] = $startMonth;
        }

        return $result;
    }

    /**
     *
     * @param $value
     * @return string|string[]
     */
    public function replaceLogicMark($value)
    {
        if (empty($value)) {
            return $value;
        }
        return str_replace(
            [
                '{^fromStartMark$}', '{^fromEndMark$}', '{^toStartMark$}', '{^toEndMark$}',
                '{^fromSymbolSMark$}', '{^fromSymbolEMark$}', '{^toSymbolSMark$}', '{^toSymbolEMark$}',
            ],
            [
                '', '', '', '',
                '(', ')', '<span style="color: #FF861C;">（', '）</span>',
            ],
            $value
        );
    }

    /**
     * 替换文件上传域名
     *
     * @param $value
     * @return string|string[]
     */
    public function replaceDomainShowUrl($value)
    {
        $domain = config('oss.domain');
        if (is_null($value)) {
            return null;
        }
        return str_replace($domain, '{DOMAIN_SHOW_URL}', $value);
    }

    /**
     * 拼接文件上传域名
     *
     * @param $value
     * @return string|string[]
     */
    public function splicingDomainShowUrl($value)
    {
        // $domain = config('domain.api');
        $domain = config('oss.domain');
        return str_replace('{DOMAIN_SHOW_URL}', $domain, $value);
    }

    /**
     * 解析url中参数信息, 返回参数数组
     *
     * @param $url
     * @return array
     */
    function convertUrlQuery($url)
    {
        $url = parse_url($url);
        $query = isset($url['query']) ? $url['query'] : '';
        $queryParts = explode('&', $query);
        $params = array();

        foreach ($queryParts as $param) {

            $item = explode('=', $param);

            $params[$item[0]] = $item[1];

        }

        return $params;
    }

    /**
     * 获取时间戳相差 年月日 时分秒
     *
     * @param $timestamp
     * @return string
     */
    public function getDiffPeriodByNowTimestamp($timestamp)
    {
        $timestamp = empty($timestamp) ? Carbon::now()->timestamp : $timestamp;
        $now = Carbon::now()->timestamp;

        if ($timestamp > $now) {
            $diffTime = $timestamp - $now;
        } else {
            $diffTime = $now - $timestamp;
        }

        $yearU = 3600 * 24 * 365;
        $monthU = 3600 * 24 * 30;
        $dayU = 3600 * 24;
        $hoursU = 3600;
        $minuteU = 60;

        if ($diffTime < $minuteU) {
            return $diffTime . '秒';
        } else if ($diffTime < $hoursU) {
            return floor($diffTime / $minuteU) . '分钟';
        } else if ($diffTime < $dayU) {
            return floor($diffTime / $hoursU) . '小时';
        } else if ($diffTime < $monthU) {
            return floor($diffTime / $dayU) . '天';
        } else if ($diffTime < $yearU) {
            return floor($diffTime / $monthU) . '月';
        } else {
            return floor($diffTime / $yearU) . '年';
        }
    }

    /**
     * 根据日期数组组装首尾搜索条件
     *
     * @param $days
     * @return string
     */
    public function getCreatetimeByDayArray($days)
    {
        // createtime
        if (1 == count($days)) {
            $start = $end = $days[0];
        } else {
            $end = array_shift($days);
            $start = array_pop($days);
        }

        $start = Carbon::createFromFormat('Y-m-d', $start)->startOfDay()->timestamp;
        $end = Carbon::createFromFormat('Y-m-d', $end)->endOfDay()->timestamp;

        return $start . ',' . $end;
    }

    /**
     * 提取富文本字符串的纯文本,并进行截取;
     * @param string $string 需要进行截取的富文本字符串
     * @param $start
     * @param $end
     * @return string|null 截取的富文本字符串
     */
    public function richToText($string, $start, $end)
    {
        if ($string) {
            // 把一些预定义的 HTML 实体转换为字符
            $html_string = htmlspecialchars_decode($string);
            // 将空格替换成空
            $content = str_replace("?", "", $html_string);
            // 去除 &nbsp;
            $content = str_replace("&nbsp;", "", $content);
            $content = str_replace("\r", "", $content);
            $content = str_replace("\n", "", $content);
            $content = str_replace("\r\n", "", $content);
            //函数剥去字符串中的 HTML、XML 以及 PHP 的标签,获取纯文本内容
            $contents = strip_tags($content);
            //返回字符串中的前$num字符串长度的字符
            return mb_strlen($contents, 'utf-8') > $start ? mb_substr($contents, $start, $end, "utf-8") : '';
        } else {
            return $string;
        }
    }

    /**
     * 生成系统不重复字符串(包含id)
     *
     * @param $id
     * @param $key
     * @param int $length
     * @return string
     */
    public function createStringById($id, $key = 'OD', $length = 32)
    {
        $id = (string)$id;
        $string = $key . date('Ymd', time());
        $strLen = strlen($string);
        $max = str_repeat('9', ($length - $strLen));
        // 兼容一下
        if ($id > $max) {
            $string .= $id;
        } else {
            $current_length = strlen($string) + strlen($id);
            $string .= $this->generateRandomNumber($length - $current_length) . $id;
        }
        return $string;
    }

    /**
     * 获取随机数字字符串
     *
     * @param $length
     * @return string
     */
    public function generateRandomNumber($length)
    {
        $chars = '0123456789';
        $string = '';
        for ($i = 0; $i < $length; $i++) {
            $string .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $string;
    }

    /**
     * 获取随机字符串
     *
     * @param $length
     * @return string
     */
    public function generateRandomString($length)
    {
        $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $string = '';
        for ($i = 0; $i < $length; $i++) {
            $string .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $string;
    }

    /**
     * 低防🙂🙂🙂b64
     *
     * @param $str
     * @param $key
     * @return string
     */
    public function enBase64Mix($str, $key)
    {
        return base64_encode($this->bindKey($str, $key));
    }

    public function deBase64Mix($str, $key)
    {
        return $this->bindKey(base64_decode($str), $key);
    }

    /**
     * 用密钥对随机化操作后的字符串进行处理
     *
     * @param $str
     * @param $key
     * @return string
     */
    private function bindKey($str, $key)
    {
        $encrypt_key = md5($key);

        $tmp = '';
        $keyLen = strlen($encrypt_key);
        $strLen = strlen($str);
        for ($i = 0, $j = 0; $i < $strLen; $i++, $j++) {
            $j = $j == $keyLen ? 0 : $j;
            $tmp .= $str[$i] ^ $encrypt_key[$j];
        }
        return $tmp;
    }

    /**
     * 获取时间戳与当前时间相差月份数
     *
     * @param $timestamp
     * @return float|int
     */
    public function getDiffMonths($timestamp)
    {
        $currentAt = Carbon::now()->timestamp;
        $months = Carbon::now()->diffInMonths(Carbon::createFromTimestamp($timestamp));
        return $currentAt > $timestamp ? -$months : $months;
    }

    /**
     * 获取时间戳与当前时间相差天数
     *
     * @param $timestamp
     * @return float|int
     */
    public function getDiffDays($timestamp)
    {
        $currentAt = Carbon::now()->timestamp;
        $days = Carbon::now()->diffInDays(Carbon::createFromTimestamp($timestamp));
        return $currentAt > $timestamp ? -$days : $days;
    }

    /**
     * 逻辑构建 0 <1 1|2|3... 月
     *
     * @param $timestamp
     * @return string
     */
    public function buildHousingLastMonths($timestamp)
    {
        $result = '0';
        $months = $this->getDiffMonths($timestamp);
        if (0 == $months) {
            $days = $this->getDiffDays($timestamp);
            if (0 == $days) {
                $result = '0';
            } else if ($days > 0) {
                $result = '<1';
            }
        } else if ($months > 0) {
            $result = (string)$months;
        } else if ($months < 0) {
            $result = '0';
        }

        return $result;
    }

    /**
     * 获取月根据amis创建时间搜索
     *
     * @param $createtime
     * @return array
     */
    public function getMonthByCreatetime($createtime)
    {
        $range = explode(',', $createtime);
        $start = $range[0];
        $end = $range[1];

        $startMonth = Carbon::createFromTimestamp($start)->startOfMonth()->format('m');
        $endMonth = Carbon::createFromTimestamp($end)->startOfMonth()->format('m');

        $result = [$startMonth];
        while ($startMonth != $endMonth) {
            // carbon bug
            $startMonth = Carbon::createFromTimestamp($start)->startOfMonth()->addMonths()->format('m');
            $start = Carbon::createFromTimestamp($start)->startOfMonth()->addMonths()->timestamp;
            $result[] = $startMonth;
        }

        return $result;
    }

    /**
     * 元心理订单各金额(包括元心币抵扣)计算
     *
     * @param $amount
     * @param $deductCoin
     * @return array
     */
    public function orderPayment($amount, $deductCoin)
    {
        if (0 == $amount) {
            // 免费
            $paymentAmount = $deductAmount = $deductCoin = '0';
        } else {
            // 付费
            // 抵扣金额(元), 元心币抵扣
            // 获取元心币 人民币折算率 目前固定为 1:10, 从配置获取吧
            // $conversionRate = '10';
            $conversionRate = config('logic.coin_conversion_rate');
            $deductAmount = '0' == $deductCoin ? '0' : bcdiv((string)$deductCoin, $conversionRate, 2);
            if ($amount > $deductAmount) {
                // 部分抵扣
                $paymentAmount = bcsub((string)$amount, (string)$deductAmount, 2);
            } else {
                // 全额抵扣
                $deductAmount = $amount;
                $paymentAmount = '0';
            }
            $deductCoin = bcmul((string)$deductAmount, $conversionRate, 2);
        }

        return [$paymentAmount, $deductAmount, $deductCoin];
    }

    /**
     * 元心币转金额(元)
     *
     * @param $coin
     * @return string|null
     */
    public function coinToAmount($coin)
    {
        $conversionRate = config('logic.coin_conversion_rate');
        return '0' == $coin ? '0' : bcdiv((string)$coin, $conversionRate, 2);
    }

    /**
     * 金额(元)转元心币
     *
     * @param $amount
     * @return string
     */
    public function amountToCoin($amount)
    {
        $conversionRate = config('logic.coin_conversion_rate');
        return bcmul((string)$amount, $conversionRate, 2);
    }
}
